#include "cimagehelper.h"

#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

extern "C" {
#ifdef USE_JPEG_LIB
    #include <jpeglib.h>
#endif

#ifdef USE_TURBO_JPEG
    #include <turbojpeg.h>
#endif
}


namespace libtool_sdk
{
#pragma pack(push)
#pragma pack(1)      //Two bytes are aligned, otherwise BMP_FILEHEADER will accounted for 16bytete
    struct bmp_fileheader
    {
        uint16_t    bfType;        //If you are not aligned, this will account for 4BYTE
        uint32_t    bfSize;
        uint16_t    bfReverved1;
        uint16_t    bfReverved2;
        uint32_t    bfOffBits;
    };

    struct bmp_infoheader
    {
        uint32_t    biSize;
        uint32_t    biWidth;
        uint32_t    biHeight;
        uint16_t    biPlanes;
        uint16_t    biBitCount;
        uint32_t    biCompression;
        uint32_t    biSizeImage;
        uint32_t    biXPelsPerMeter;
        uint32_t    biYpelsPerMeter;
        uint32_t    biClrUsed;
        uint32_t    biClrImportant;
    };
#pragma pack(pop)

#define OUT_LOG(fmt,...) fprintf(stderr,  "%s:: " fmt "\n", __FUNCTION__, ##__VA_ARGS__);

TOOL_SDK_API int DLL_SPEC simplest_bgr24_to_bmp(unsigned char* pSrcBuffer, int iBufferLen, int width, int height, unsigned char* pDestBuffer, int*OutPutLength)
{
    typedef struct
    {
        int32_t imageSize;
        int32_t blank;
        int32_t startPosition;
    }BmpHead;

    typedef struct
    {
        int32_t  Length;
        int32_t  width;
        int32_t  height;
        uint16_t  colorPlane;
        uint16_t  bitColor;
        int32_t  zipFormat;
        int32_t  realSize;
        int32_t  xPels;
        int32_t  yPels;
        int32_t  colorUse;
        int32_t  colorImportant;
    }InfoHead;

    //int i = 0, j = 0;
    BmpHead m_BMPHeader = { 0, 0, 0 };

    InfoHead  m_BMPInfoHeader = { 0 , 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    char bfType[2] = { 'B', 'M' };
    int header_size = sizeof(bfType) + sizeof(BmpHead) + sizeof(InfoHead);
    unsigned char *rgb24_buffer = pSrcBuffer;
    unsigned char *pDest = pDestBuffer;

    m_BMPHeader.imageSize = 3 * width*height + header_size;
    m_BMPHeader.startPosition = header_size;

    m_BMPInfoHeader.Length = sizeof(InfoHead);
    m_BMPInfoHeader.width = width;
    //BMP storage pixel data in opposite direction of Y-axis (from bottom to top).
    m_BMPInfoHeader.height = -height;
    m_BMPInfoHeader.colorPlane = 1;
    m_BMPInfoHeader.bitColor = 24;
    m_BMPInfoHeader.realSize = 3 * width*height;

    memcpy(pDest, bfType, sizeof(bfType));
    pDest += sizeof(bfType);
    memcpy(pDest, &m_BMPHeader, sizeof(m_BMPHeader));
    pDest += sizeof(m_BMPHeader);
    memcpy(pDest, &m_BMPInfoHeader, sizeof(m_BMPInfoHeader));
    pDest += sizeof(m_BMPInfoHeader);

    *OutPutLength = 3 * width*height + sizeof(bfType) + sizeof(m_BMPHeader) + sizeof(m_BMPInfoHeader);

//    BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2
//    It saves pixel data in Little Endian
//    So we change 'R' and 'B'
    for (int j = 0; j<height; j++)
    {
        for (int i = 0; i<width; i++)
        {
            char temp = rgb24_buffer[(j*width + i) * 3 + 2];
            rgb24_buffer[(j*width + i) * 3 + 2] = rgb24_buffer[(j*width + i) * 3 + 0];
            rgb24_buffer[(j*width + i) * 3 + 0] = temp;
        }
    }

    memcpy(pDest, rgb24_buffer, iBufferLen);

    return 0;
}

#ifdef USE_JPEG_LIB
TOOL_SDK_API int DLL_SPEC jpeg_decompress_to_rgb24(unsigned char *pjpgBuffer, int jpgSize, void **pRGBBuffer, unsigned long *pOutSize, int *outWidth, int *outHeigth)
{
    if (pjpgBuffer[0] != 0xFFu || pjpgBuffer[1] != 0xD8u)
    {
        OUT_LOG("Bad jpeg start with: %#x%x", pjpgBuffer[0], pjpgBuffer[1]);
        return -1;
    }

    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    OUT_LOG("jpeg_create_decompress");
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, pjpgBuffer, jpgSize);

    OUT_LOG("jpeg_read_header");
    int rc = jpeg_read_header(&cinfo, true);
    if (rc != 1)
    {
        OUT_LOG( "File does not seem to be a normal JPEG");
        jpeg_destroy_decompress(&cinfo);
        return -2;
    }
    OUT_LOG("jpeg_start_decompress");
    int row_stride = 0, width = 0, height = 0, pixel_size = 0;

    jpeg_start_decompress(&cinfo);
    width = cinfo.output_width;
    height = cinfo.output_height;
    pixel_size = cinfo.output_components;

    OUT_LOG("output_width = %d, output_height = %d,output_components = %d ",
            cinfo.output_width,
            cinfo.output_height,
           cinfo.output_components);

    unsigned long bmp_size = 0;
    unsigned char *bmp_buffer = 0;
    bmp_size = width * height * pixel_size;
    bmp_buffer = new unsigned char[bmp_size];
    memset(bmp_buffer, 0, bmp_size);

    row_stride = width * pixel_size;

    while (cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = bmp_buffer + (cinfo.output_scanline) * row_stride;

        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }
    OUT_LOG("finish jpeg_read_scanlines");
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);

    OUT_LOG("finish jpeg_destroy_decompress");
    *pRGBBuffer = bmp_buffer;
    *pOutSize = bmp_size;
    *outWidth = width;
    *outHeigth = height;

    return 0;
}

TOOL_SDK_API unsigned char * DLL_SPEC rbg24_Stretch_Linear( unsigned char *srcRGBData, int w_Src, int h_Src, int bit_depth, int w_Dest, int h_Dest)
{
    int sw = w_Src-1, sh = h_Src-1, dw = w_Dest-1, dh = h_Dest-1;
    int B, N, x, y;
    int nPixelSize = bit_depth/8;
    unsigned char *pLinePrev,*pLineNext;
    unsigned char *pDest = new unsigned char[w_Dest*h_Dest*bit_depth/8];
    unsigned char *tmp;
    unsigned char *pA,*pB,*pC,*pD;

    for(int i=0;i<=dh;++i)
    {
        tmp =pDest + i*w_Dest*nPixelSize;
        y = i*sh/dh;
        N = dh - i*sh%dh;
        pLinePrev = srcRGBData + (y++)*w_Src*nPixelSize;
        //pLinePrev =(unsigned char *)aSrc->m_bitBuf+((y++)*aSrc->m_width*nPixelSize);
        pLineNext = (N==dh) ? pLinePrev : srcRGBData+y*w_Src*nPixelSize;
        //pLineNext = ( N == dh ) ? pLinePrev : (unsigned char *)aSrc->m_bitBuf+(y*aSrc->m_width*nPixelSize);
        for(int j=0;j<=dw;++j)
        {
            x = j*sw/dw*nPixelSize;
            B = dw-j*sw%dw;
            pA = pLinePrev+x;
            pB = pA+nPixelSize;
            pC = pLineNext + x;
            pD = pC + nPixelSize;
            if(B == dw)
            {
                pB=pA;
                pD=pC;
            }

            for(int k=0;k<nPixelSize;++k)
            {
                *tmp++ = ( unsigned char )( int )(
                            ( B * N * ( *pA++ - *pB - *pC + *pD ) + dw * N * *pB++
                              + dh * B * *pC++ + ( dw * dh - dh * B - dw * N ) * *pD++
                              + dw * dh / 2 ) / ( dw * dh ) );
            }
        }
    }
    return pDest;
}

TOOL_SDK_API int DLL_SPEC rbg24_to_jpeg(unsigned char *srcRGBData, int w_Src, int h_Src, void** ppJpegBuffer, unsigned long *pOutSize)
{
    if(NULL == srcRGBData
            || w_Src < 8
            || h_Src < 8
            || pOutSize == NULL)
    {
        OUT_LOG("parameter is invalid.");
        return -1;
    }

    jpeg_compress_struct toWriteInfo;
    jpeg_error_mgr errorMgr;
    toWriteInfo.err = jpeg_std_error(&errorMgr);
    //注册失败的回调函数
    //toWriteInfo.err->error_exit = error_exit;
    jpeg_create_compress(&toWriteInfo);

    //确定要用于输出压缩的jpeg的数据空间
    unsigned char* pBuffer = nullptr;
    unsigned long iOutSize = 0;
    jpeg_mem_dest(&toWriteInfo, &pBuffer, &iOutSize);

    toWriteInfo.image_width = w_Src;
    toWriteInfo.image_height = h_Src;
    // toWriteInfo.jpeg_width = nWidth ;
    // toWriteInfo.jpeg_height = nHeight;
    toWriteInfo.input_components = 3;// 在此为1,表示灰度图， 如果是彩色位图，则为4
    // toWriteInfo.in_color_space = JCS_EXT_BGR; //JCS_GRAYSCALE表示灰度图，JCS_RGB表示彩色图像
    toWriteInfo.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图，JCS_RGB表示彩色图像

    jpeg_set_defaults(&toWriteInfo);
    jpeg_set_quality(&toWriteInfo, 40, TRUE);	//设置压缩质量100表示100%
    jpeg_start_compress(&toWriteInfo, TRUE);

    int nRowStride = ((w_Src * 24 + 31) >> 5) << 2;;	// 如果不是索引图,此处需要乘以4
    JSAMPROW row_pointer[1];	// 一行位图
    unsigned char* pSrc = srcRGBData + h_Src*nRowStride;   //用于处理JPEG 倒转的情况
    //void* pSrc = srcRGBData;
    while (toWriteInfo.next_scanline < toWriteInfo.image_height)
    {
//        row_pointer[0] = (JSAMPROW)((unsigned char*)pSrc + toWriteInfo.next_scanline*nRowStride);
        row_pointer[0] = (JSAMPROW)((unsigned char*)pSrc - toWriteInfo.next_scanline*nRowStride);  //用于处理JPEG 倒转的情��??
        jpeg_write_scanlines(&toWriteInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&toWriteInfo);
    jpeg_destroy_compress(&toWriteInfo);

    if(iOutSize > 0)
    {
        *ppJpegBuffer = new unsigned char[iOutSize];
        memcpy(*ppJpegBuffer, pBuffer, iOutSize);
        *pOutSize  = iOutSize;

        free(pBuffer);
        pBuffer = NULL;

//        *pOutSize = 0;
    }
    else
    {
        *pOutSize = 0;
        printf("%s failed.\n", __FUNCTION__);
    }

    return 0;
}

TOOL_SDK_API int DLL_SPEC Bmp_to_jpeg(void* lpBmpBuffer, int nWidth, int nHeight,  void** ppJpegBuffer,  unsigned long* pOutSize)
{
    if(NULL == lpBmpBuffer)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }
    struct bmp_fileheader bfh;
    struct bmp_infoheader bih;

    memcpy(&bfh, (unsigned char*)lpBmpBuffer, sizeof(bmp_fileheader));
    memcpy(&bih, (unsigned char*)lpBmpBuffer+ sizeof(bmp_fileheader) , sizeof(bmp_infoheader));
    int iWidth = bih.biWidth;
    int iHeight = bih.biHeight;
    if(bih.biBitCount == 24)
    {
        unsigned char *srcRGBData = (unsigned char *)lpBmpBuffer + bfh.bfOffBits;
        return rbg24_to_jpeg(srcRGBData, iWidth, iHeight, ppJpegBuffer, pOutSize);
    }
    else
    {
        OUT_LOG("bmp bitcount (%u) != 24, return -1", bih.biBitCount);
        return -2;
    }
}

TOOL_SDK_API int DLL_SPEC jpeg_decompress_to_bmp(unsigned char *pjpgBuffer, int jpgSize, void **pBmpBuffer, unsigned long *pOutSize,  int* outWidth, int* outHeigth)
{
    if (pjpgBuffer[0] != 0xFFu || pjpgBuffer[1] != 0xD8u)
    {
        OUT_LOG("Bad jpeg start with: %#x%x", pjpgBuffer[0], pjpgBuffer[1]);
        return -1;
    }

    unsigned long rgb24_size = 0;
    unsigned char *rgb24_buffer = NULL;
    int src_width = 0;
    int src_height = 0;
    int src_pixel_size = 3;

    if(0 != jpeg_decompress_to_rgb24(pjpgBuffer, jpgSize,(void**)&rgb24_buffer, &rgb24_size, &src_width, &src_height)
            || rgb24_buffer == NULL
            || rgb24_size <= 0)
    {
        OUT_LOG("decompress to rgb24 failed.");
        return -2;
    }
    OUT_LOG("src_width = %d, src_height = %d,rgb data = %p,  size = %d ",
            src_width,
            src_height,
            rgb24_buffer,
            rgb24_size);

    unsigned char *destBmpBuf = new unsigned char[rgb24_size*2];
    int iBufferLength = rgb24_size*2;

    if (destBmpBuf)
    {
        memset(destBmpBuf, 0, iBufferLength);
    }
    simplest_bgr24_to_bmp(rgb24_buffer, rgb24_size, src_width, src_height, destBmpBuf, &iBufferLength);

    *pBmpBuffer = destBmpBuf;
    *pOutSize = iBufferLength;
    *outWidth = src_width;
    *outHeigth = src_height;

    if(rgb24_buffer)
    {
        delete[] rgb24_buffer;
        rgb24_buffer = NULL;
    }

    OUT_LOG("after bgr24_to_bmp, final size = %d", iBufferLength);
    return 0;
}

TOOL_SDK_API int DLL_SPEC jpeg_resize_to_bmp(unsigned char *pjpgBuffer, int jpgSize, int destW, int destH, void **pBmpBuffer, unsigned long *pOutSize)
{
    if(NULL == pjpgBuffer
            || jpgSize <= 0
            || destW < 8
            || destH < 8
            || pOutSize == NULL)
    {
        OUT_LOG("parameter is invalid");
        return -1;
    }

    if (pjpgBuffer[0] != 0xFFu || pjpgBuffer[1] != 0xD8u)
    {
        OUT_LOG("Bad jpeg start with: %#x%x", pjpgBuffer[0], pjpgBuffer[1]);
        return -1;
    }

    unsigned long rgb24_size = 0;
    unsigned char *rgb24_buffer = NULL;
    int src_width = 0;
    int src_height = 0;
    int src_pixel_size = 3;

    if(0 != jpeg_decompress_to_rgb24(pjpgBuffer, jpgSize,(void**)&rgb24_buffer, &rgb24_size, &src_width, &src_height)
            || rgb24_buffer == NULL
            || rgb24_size <= 0)
    {
        OUT_LOG("decompress to rgb24 failed.");
        return -2;
    }
    OUT_LOG("src_width = %d, src_height = %d,rgb data = %p,  size = %d ",
            src_width,
            src_height,
            rgb24_buffer,
            rgb24_size);

    //strech rgb data
    int iDestW = destW;
    int iDestH = destH;
    int iStrechSize = iDestW*iDestH*3;
    OUT_LOG("destW = %d, dest H = %d, strechSize = %d", iDestW,iDestH, iStrechSize );

    unsigned char *pStrechData =  rbg24_Stretch_Linear(rgb24_buffer, src_width, src_height,  src_pixel_size * 8, iDestW,  iDestH );

    unsigned char *destOutBuf = new unsigned char[iStrechSize*2];
    int iBufferLength = iStrechSize*2;

    simplest_bgr24_to_bmp(pStrechData, iStrechSize, iDestW, iDestH, destOutBuf, &iBufferLength);

    *pBmpBuffer = destOutBuf;
    *pOutSize = iBufferLength;

    if(pStrechData)
    {
        delete[] pStrechData;
        pStrechData = NULL;
    }

    if(rgb24_buffer)
    {
        delete[] rgb24_buffer;
        rgb24_buffer = NULL;
    }

    return 0;
}

TOOL_SDK_API int DLL_SPEC jpeg_resize(unsigned char *pjpgBuffer, int jpgSize, int destW, int destH, void **pOutBuffer, unsigned long *pOutSize)
{
    unsigned char* pBmpBuffer = NULL;
    unsigned long iBmpBufLen = 0;
    int iRet =  jpeg_resize_to_bmp(pjpgBuffer,  jpgSize,  destW,  destH, (void **)&pBmpBuffer, &iBmpBufLen);

    if(iRet != 0)
    {
        if(pBmpBuffer)
        {
            delete [] pBmpBuffer;
            pBmpBuffer = NULL;
        }

        OUT_LOG("jpeg_resize_to_bmp failed.");
        return -1;
    }

    OUT_LOG("jpeg_resize_to_bmp success, bmp size = %d", iBmpBufLen);

    iRet =  Bmp_to_jpeg(pBmpBuffer, destW, destH,  pOutBuffer, pOutSize);
    if(iRet != 0)
    {
        OUT_LOG("Bmp_to_jpeg failed.");
    }

    if(pBmpBuffer)
    {
        delete [] pBmpBuffer;
        pBmpBuffer = NULL;
    }
    return iRet;
}

TOOL_SDK_API int DLL_SPEC bmp_resize(unsigned char *srcBmp, int srcSize, int srcW, int srcH, void **pOutBuffer, unsigned long *pOutSize, int destW, int destH)
{
    unsigned char * srcRgbData = srcBmp + 54;

    unsigned char *strechData = rbg24_Stretch_Linear(srcRgbData, srcW, srcH, 24, destW,destH );
    if(NULL == strechData)
    {
        OUT_LOG("rbg24_Stretch_Linear from (%d, %d) to (%d, %d) failed.",srcW, srcH, destW,destH);
        return -1;
    }
    int iStrechDataLen = srcW * srcH * 3;

    unsigned char *bmpBuf = new unsigned char[iStrechDataLen*2];
    int iBmpBufLen = iStrechDataLen*2;
    int iRet = simplest_bgr24_to_bmp(strechData, iStrechDataLen, srcW, srcH, bmpBuf, &iBmpBufLen);
    if(iRet == 0)
    {
        OUT_LOG("simplest_bgr24_to_bmp success, size = %d.",iBmpBufLen);

        *pOutBuffer = bmpBuf;
        *pOutSize = iBmpBufLen;
    }
    else
    {
        OUT_LOG("simplest_bgr24_to_bmp failed.");
        if(bmpBuf)
        {
            delete[] bmpBuf;
            bmpBuf = NULL;
        }
    }

    if(strechData)
    {
        delete[] strechData;
        strechData = NULL;
    }

    return iRet;
}

TOOL_SDK_API int DLL_SPEC Bmp_to_jpeg_gray(void *lpBmpBuffer, int nWidth, int nHeight, unsigned char **ppJpegBuffer, unsigned long *pOutSize)
{
    if(NULL == lpBmpBuffer
            || nWidth < 8
            || nHeight < 8
            || pOutSize == NULL)
    {
        OUT_LOG("parameter is invalid.");
        return -1;
    }

    struct bmp_fileheader bfh;
    struct bmp_infoheader bih;

    memcpy(&bfh, (unsigned char*)lpBmpBuffer, sizeof(bmp_fileheader));
    memcpy(&bih, (unsigned char*)lpBmpBuffer+ sizeof(bmp_fileheader) , sizeof(bmp_infoheader));

    OUT_LOG("parse bmp file header: bfType = %x, bfSize = %u, bfReverved1= %x, bfReverved2 = %x, bfOffBits = %u ",
            bfh.bfType,
            bfh.bfSize,
            bfh.bfReverved1,
            bfh.bfReverved2,
            bfh.bfOffBits
            );
    OUT_LOG("bmp info header: biSize = %u ",bih.biSize);
    OUT_LOG("bmp info header: biWidth = %u ",bih.biWidth);
    OUT_LOG("bmp info header: biHeight = %u ",bih.biHeight);
    OUT_LOG("bmp info header: biPlanes = %u ",bih.biPlanes);
    OUT_LOG("bmp info header: biBitCount = %u ",bih.biBitCount);
    OUT_LOG("bmp info header: biCompression = %u ",bih.biCompression);
    OUT_LOG("bmp info header: biSizeImage = %u ",bih.biSizeImage);
    OUT_LOG("bmp info header: biXPelsPerMeter = %u ",bih.biXPelsPerMeter);
    OUT_LOG("bmp info header: biYpelsPerMeter = %u ",bih.biYpelsPerMeter);
    OUT_LOG("bmp info header: biClrUsed = %u ",bih.biClrUsed);
    OUT_LOG("bmp info header: biClrImportant = %u ",bih.biClrImportant);

    int w_Src = bih.biWidth;
    int h_Src = bih.biHeight;
    unsigned short depth = bih.biBitCount;
    unsigned char*srcRGBData = (unsigned char*)lpBmpBuffer + bfh.bfOffBits;

    const int BIN_BIT_COUNT = 8;
    const int BIN_WIDTH = 112;
    const int BIN_HEIGHT = 20;
    const int BIN_STRIDE = BIN_WIDTH / BIN_BIT_COUNT;
    const int BIN_BYTE_COUNT = BIN_HEIGHT * BIN_STRIDE;

    int iStride = ((BIN_STRIDE + 3) >> 2) << 2;

    unsigned char* pRGB_8 = new unsigned char[iStride*h_Src*depth*8];
    memset(pRGB_8, 0, w_Src*h_Src*depth);

    unsigned char* pSrcRgb = srcRGBData;
    unsigned char* pDest = pRGB_8;
    int iValue = 0;
    for(int i = 0 ; i<h_Src; i++ )
    {
        for(int j = 0; j < iStride; j++)
        {

            unsigned char bTemp = pSrcRgb[i * iStride +j];
            *pDest = (bTemp & 0x80) == 0? 0 : 0xff;
            *(pDest +1) = (bTemp & 0x40) == 0? 0 : 0xff;
            *(pDest +2)  = (bTemp & 0x20) == 0? 0 : 0xff;
            *(pDest +3)  = (bTemp & 0x20) == 0? 0 : 0xff;
            *(pDest +4)  = (bTemp & 0x10) == 0? 0 : 0xff;
            *(pDest +5)  = (bTemp & 0x8) == 0? 0 : 0xff;
            *(pDest +6)  = (bTemp & 0x4) == 0? 0 : 0xff;
            *(pDest +7)  = (bTemp & 0x1) == 0? 0 : 0xff;

            pDest += 8;
            iValue += 8;
        }
    }
    OUT_LOG("iValue = %d",iValue);


    OUT_LOG("finish convert");

    jpeg_compress_struct toWriteInfo;
    jpeg_error_mgr errorMgr;
    toWriteInfo.err = jpeg_std_error(&errorMgr);
    //注册失败的回调函��??
    //toWriteInfo.err->error_exit = error_exit;
    jpeg_create_compress(&toWriteInfo);

    //确定要用于输出压缩的jpeg的数据空��??
    unsigned char* pBuffer = NULL;
    unsigned long iOutSize = 0;
    jpeg_mem_dest(&toWriteInfo, &pBuffer, &iOutSize);

    toWriteInfo.image_width = iStride *8;
    toWriteInfo.image_height = h_Src;
//    toWriteInfo.jpeg_width = nWidth ;
//    toWriteInfo.jpeg_height = nHeight;
    toWriteInfo.input_components = 1;// 在此��??1,表示灰度图， 如果是彩色位图，则为4
    toWriteInfo.in_color_space = JCS_GRAYSCALE; //JCS_GRAYSCALE 表示灰度图，JCS_RGB表示彩色图像
    //toWriteInfo.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图，JCS_RGB表示彩色图像

    jpeg_set_defaults(&toWriteInfo);
    jpeg_set_quality(&toWriteInfo, 100, TRUE);	//设置压缩质量100表示100%
    jpeg_start_compress(&toWriteInfo, TRUE);

    //int nRowStride = ((w_Src * 24 + 31) >> 5) << 2;;	// 如果不是索引��??,此处需要乘��??4
    int nRowStride = ((toWriteInfo.image_width + 3) >> 2)<<2;	// 如果不是索引��??,此处需要乘��??4
//    int nRowStride = BIN_STRIDE;
    JSAMPROW row_pointer[1];	// 一行位��??
    unsigned char* pSrc = pRGB_8 + h_Src*nRowStride;   //用于处理JPEG 倒转的情��??
//    unsigned char* pSrc = /*srcRGBData*/ pRGB_8;

    OUT_LOG("start jpeg_write_scanlines");
    while (toWriteInfo.next_scanline < toWriteInfo.image_height)
    {
        OUT_LOG("toWriteInfo.next_scanline = %u, nRowStride = %u", toWriteInfo.next_scanline, nRowStride);

//        row_pointer[0] = (JSAMPROW)((unsigned char*)pSrc + toWriteInfo.next_scanline*nRowStride);
        row_pointer[0] = (JSAMPROW)((unsigned char*)pSrc - toWriteInfo.next_scanline*nRowStride);  //用于处理JPEG 倒转的情��??
        jpeg_write_scanlines(&toWriteInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&toWriteInfo);
    jpeg_destroy_compress(&toWriteInfo);

//    JSAMPARRAY buffer;
//    unsigned short depth = 1;
//    buffer=(*toWriteInfo.mem->alloc_sarray)  ((j_common_ptr)&toWriteInfo,JPOOL_IMAGE,w_Src*h_Src,1);
//    unsigned char *point;
//    point=pSrc+w_Src*depth*(h_Src-1);
//    while (toWriteInfo.next_scanline<h_Src)
//    {
//        memcpy(*buffer,point,w_Src*depth);
//        jpeg_write_scanlines(&toWriteInfo,buffer,1);
//        point -= w_Src*depth;
//    }

    delete[] pRGB_8;
    pRGB_8 = NULL;

    if(iOutSize > 0)
    {
        *ppJpegBuffer = new unsigned char[iOutSize];
        memcpy(*ppJpegBuffer, pBuffer, iOutSize);
        *pOutSize  = iOutSize;

        free(pBuffer);
        pBuffer = NULL;

//        *pOutSize = 0;
    }
    else
    {
        *pOutSize = 0;
        printf("%s failed.\n", __FUNCTION__);
    }

    return 0;
}

TOOL_SDK_API int DLL_SPEC libjpeg_xlwbin_to_jpeg(unsigned char *pBinData, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    cinfo.image_width = width;  /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3; /* # of color components per pixel */
    cinfo.in_color_space = JCS_YCbCr; /* colorspace of input image */

    unsigned char *pjpegData = NULL;
    unsigned long outLength;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 70, TRUE);
    jpeg_mem_dest(&cinfo, &pjpegData, &outLength);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW jrow[1];    /* pointer to a single row */
    unsigned char *buf = new unsigned char[width * 3];

    for(unsigned int j=0;j<cinfo.image_height;j++ )
    {
        for (unsigned int i = 0; i < cinfo.image_width; i++)
        {
            unsigned char cy = pBinData[ (j * cinfo.image_width + i) >> 3 ];
            int r = (j * cinfo.image_width + i) & 0x07;
            unsigned char cy2 = (( cy & (0x01 << r)) >> r) ? 255 : 0;

            buf[i*3] = cy2;
            buf[i*3+1] = 0x80;
            buf[i*3+2] = 0x80;
        }

        jrow[0] = buf;
        jpeg_write_scanlines(&cinfo, jrow, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    if(outLength != 0)
    {
        *ppJpegData = new unsigned char[outLength];
        memcpy(*ppJpegData, pjpegData, outLength);
        *nJpegLen = outLength;

        free(pjpegData);
        pjpegData = NULL;
    }
    else
    {
        *nJpegLen = 0;
    }

    delete[] buf;
    buf = NULL;

    return 0;
}

TOOL_SDK_API int DLL_SPEC libjpeg_yuv422_to_jpeg(unsigned char *pYUV422Data, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    cinfo.image_width = width;  /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3; /* # of color components per pixel */
    cinfo.in_color_space = JCS_YCbCr; /* colorspace of input image */

    unsigned char *pjpegData = NULL;
    unsigned long outLength;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 92, TRUE);
    jpeg_mem_dest(&cinfo, &pjpegData, &outLength);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW jrow[1];    /* pointer to a single row */
    unsigned char *buf = new unsigned char[width * 3];
    unsigned char* pY = pYUV422Data;
    unsigned char* pU = pYUV422Data + cinfo.image_width*cinfo.image_height;
    unsigned char* pV = pU + (cinfo.image_width /2) * cinfo.image_height;
    while (cinfo.next_scanline < cinfo.image_height)
    {
        for (unsigned int i = 0; i < cinfo.image_width; i++)
        {
            buf[i*3] = pY[i];
            buf[i*3+1] = pU[i/2];
            buf[i*3+2] = pV[i/2];
        }
        pY += cinfo.image_width;
        pU += cinfo.image_width / 2;
        pV += cinfo.image_width / 2;

        jrow[0] = buf;
        jpeg_write_scanlines(&cinfo, jrow, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    if(outLength != 0)
    {
        *ppJpegData = new unsigned char[outLength];
        memcpy(*ppJpegData, pjpegData, outLength);
        *nJpegLen = outLength;

        free(pjpegData);
        pjpegData = NULL;
    }
    else
    {
        *nJpegLen = 0;
    }

    delete[] buf;
    buf = NULL;

    return 0;
}

#endif


#ifdef USE_TURBO_JPEG

TOOL_SDK_API int DLL_SPEC turbo_yuv422_to_jpg(unsigned char *pYUV422Data, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    tjhandle hProcess =  tjInitCompress();

    int flags = TJ_YUV;
    int padding = 1; // 1��??4均可，但不能��??0
    int need_size = 0;
    int ret = 0;
    int subsamp = TJSAMP_422;

    need_size = tjBufSizeYUV2(width, padding, height, TJSAMP_422);

    unsigned char *pJpegData = NULL;
    unsigned long OutLen = 0;

    unsigned char *srcPlanes[3];
    int strides[3], retval = -1;

    if (pYUV422Data == NULL
            || width <= 0
            || padding < 1
            || height <= 0
            || subsamp < 0
            || subsamp >= NUMSUBOPT)
    {
        printf("tjCompressFromYUV(): Invalid argument \n");
        return 1;
    }

    srcPlanes[0] = pYUV422Data;
    srcPlanes[1] = pYUV422Data + width*height;
    srcPlanes[2] = srcPlanes[1] + (width / 2) * height;

    strides[0] = width;
    strides[1] = width / 2;
    strides[2] = width / 2;

    // The original small image YUV data of xin_lu_wei is not aligned , so the tjCompressFromYUV interface cannot be used directly
    ret=  tjCompressFromYUVPlanes(
                hProcess,
                srcPlanes,
                width,
                strides,
                height,
                subsamp,
                &pJpegData,
                &OutLen,
                50,
                flags);

    if (ret < 0)
    {
        printf("compress to jpeg failed: %s\n", tjGetErrorStr());
    }
    else
    {
        //OUT_LOG("tjCompressFromYUV = %d, OutLen = %lu", ret, OutLen);
        *ppJpegData = new unsigned char[OutLen];
        memcpy(*ppJpegData, pJpegData, OutLen);
        *nJpegLen = OutLen;

        //free(pJpegData);
        //pJpegData = NULL;
        tjFree(pJpegData);
    }

    tjDestroy(hProcess);
    return ret;

bailout:
    return retval;
}

TOOL_SDK_API int DLL_SPEC turbo_rbg24_to_jpeg(unsigned char *srcRGBData, int w_Src, int h_Src, unsigned char** ppJpegBuffer, unsigned long* pOutSize)
{
    tjhandle pJpgProcessor =  tjInitCompress();

    long unsigned int iOutjpegSize = 0;
    unsigned char* pCompressedImage = NULL; //!< Memory is allocated by tjCompress2 if _jpegSize == 0
    int iRet = tjCompress2(pJpgProcessor,
        srcRGBData,
        w_Src,
        0,
        h_Src,
        TJPF_BGR,
        &pCompressedImage,
        &iOutjpegSize,
        TJSAMP_444, 70, TJFLAG_FASTDCT);
    tjDestroy(pJpgProcessor);

    if (iOutjpegSize != 0 && pCompressedImage != NULL)
    {
        *ppJpegBuffer = new unsigned char[iOutjpegSize];
        memcpy(*ppJpegBuffer, pCompressedImage, iOutjpegSize);
        *pOutSize = iOutjpegSize;
    }
    else
    {
        *pOutSize = 0;
    }
    tjFree(pCompressedImage);

    return iRet;
}

TOOL_SDK_API int DLL_SPEC turbo_bmp_to_jpg(void* lpBmpBuffer, int nWidth, int nHeight, unsigned char** ppJpegBuffer, unsigned long* pOutSize)
{
    if (NULL == lpBmpBuffer
        || 0 == nWidth
        || 0 == nHeight)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }
    struct bmp_fileheader bfh;
    struct bmp_infoheader bih;

    memcpy(&bfh, (unsigned char*)lpBmpBuffer, sizeof(bmp_fileheader));
    memcpy(&bih, (unsigned char*)lpBmpBuffer + sizeof(bmp_fileheader), sizeof(bmp_infoheader));

    if (bih.biBitCount == 24)
    {
        unsigned char *srcRGBData = (unsigned char *)lpBmpBuffer + bfh.bfOffBits;
        return turbo_rbg24_to_jpeg(srcRGBData, nWidth, nHeight, ppJpegBuffer, pOutSize);
    }
    else
    {
        OUT_LOG("bmp bitcount (%u) != 24, return -1", bih.biBitCount);
        return -2;
    }
}

TOOL_SDK_API int DLL_SPEC turbo_xlw_bin_to_jpg(unsigned char *pBinaryData, const unsigned int width, const unsigned int height, unsigned char **ppJpegData, unsigned long *nJpegLen)
{
    if (NULL == pBinaryData
        || 0 == width
        || 0 == height)
    {
        OUT_LOG("NULL == lpBmpBuffer or parameter is invalid.");
        return -1;
    }

    //The original data only has "Y" information, and 8 pixels are compressed into 1, so we need to expand it and fill in the "U" and "V" data
    unsigned char *pYUVbuf = new unsigned char[height* width * 3];
    if(pYUVbuf == NULL)
    {
        OUT_LOG("NULL == buf.");
        return -1;
    }
    memset(pYUVbuf, 0x80, height* width * 3);       //filling gray color

    for( int j=0;j<height;j++ )
    {
        for (int i = 0; i < width; i++)
        {
            unsigned char cy = pBinaryData[ (j * width + i) >> 3 ];
            int r = (j * width + i) & 0x07;
            unsigned char cy2 = (( cy & (0x01 << r)) >> r) ? 255 : 0;

            //filling as yuv 444 packed
//            buf[j*width*3 + i*3] = cy2;
//            buf[j*width*3 + i*3+1] = 0x80;
//            buf[j*width*3 + i*3+2] = 0x80;

            //filling as yuv 444 planar
             pYUVbuf[j*width + i] = cy2;
             pYUVbuf[height* width+ j*width + i] = 0x80;
             pYUVbuf[2*height* width+ j*width + i] = 0x80;
        }
    }

    tjhandle hProcess =  tjInitCompress();

    int flags = TJ_YUV;
    int padding = 1; // 1��??4均可，但不能��??0
    int need_size = 0;
    int ret = 0;
    int subsamp = TJSAMP_444;

    need_size = tjBufSizeYUV2(width, padding, height, TJSAMP_444);

    unsigned char *pJpegData = NULL;
    unsigned long OutLen = 0;

    unsigned char* pSrcYUVData = pYUVbuf;

    //tjCompressFromYUV only support YUV planar compress
    ret=  tjCompressFromYUV(
                hProcess,
                pSrcYUVData,
                width,
                padding,
                height,
                subsamp,
                &pJpegData,
                &OutLen,
                100,
                flags);

    delete[] pYUVbuf;
    pYUVbuf = NULL;

    if (ret < 0)
    {
        printf("compress to jpeg failed: %s\n", tjGetErrorStr());
    }
    else
    {
        *ppJpegData = new unsigned char[OutLen];
        memcpy(*ppJpegData, pJpegData, OutLen);
        *nJpegLen = OutLen;

        tjFree(pJpegData);
    }

    tjDestroy(hProcess);
    return ret;
}

#endif

}   //namespace
